/*
 * CC3VertexArrays.m
 *
 * cocos3d 0.6.1
 * Author: Bill Hollings
 * Copyright (c) 2010-2011 The Brenwill Workshop Ltd. All rights reserved.
 * http://www.brenwill.com
 *
 * Permission is hereby granted, free of charge, to any person obtaining a copy
 * of this software and associated documentation files (the "Software"), to deal
 * in the Software without restriction, including without limitation the rights
 * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
 * copies of the Software, and to permit persons to whom the Software is
 * furnished to do so, subject to the following conditions:
 * 
 * The above copyright notice and this permission notice shall be included in
 * all copies or substantial portions of the Software.
 * 
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
 * THE SOFTWARE.
 *
 * http://en.wikipedia.org/wiki/MIT_License
 * 
 * See header file CC3VertexArrays.h for full API documentation.
 */

#include "CC3VertexArrays.h"
#include "CC3OpenGLES11Utility.h"
#include "CC3OpenGLES11Engine.h"

CC3VertexArray::CC3VertexArray() :
	elements(NULL)
{
}

CC3VertexArray::~CC3VertexArray()
{
}

GLvoid* CC3VertexArray::getElements()
{
	return elements;
}

void CC3VertexArray::setElements(GLvoid* newValue)
{
	deallocateElements();		// Safely disposes existing elements
	elements = newValue;
}

void CC3VertexArray::deallocateElements()
{
	if (elementsAreRetained && elements) 
	{
		free(elements);
		elements = NULL;
		elementsAreRetained = false;
		//LogTrace(@"%@ deallocated %u previously allocated elements", self, elementCount);
	}
}

bool CC3VertexArray::initWithTag(GLuint aTag, const char* aName)
{
	if ( CC3Identifiable::initWithTag(aTag, aName) ) 
	{
		elements = NULL;
		elementCount = 0;
		elementType = GL_FLOAT;
		elementSize = 3;
		elementStride = 0;
		bufferID = 0;
		bufferUsage = GL_STATIC_DRAW;
		elementOffset = 0;
		elementsAreRetained = false;
		shouldAllowVertexBuffering = true;
		shouldReleaseRedundantData = true;
		
		return true;
	}
	
	return false;
}

GLsizei CC3VertexArray::getElementCount()
{
	return elementCount;
}

void CC3VertexArray::setElementCount(GLsizei newValue)
{
	elementCount = newValue;
}

GLuint CC3VertexArray::getElementOffset()
{
	return elementOffset;
}

void CC3VertexArray::setElementOffset(GLuint newValue)
{
	elementOffset = newValue;
}

GLint CC3VertexArray::getElementSize()
{
	return elementSize;
}

void CC3VertexArray::setElementSize(GLint newValue)
{
	elementSize = newValue;
}

GLenum CC3VertexArray::getElementType()
{
	return elementType;
}

void CC3VertexArray::setElementType(GLenum newValue)
{
	elementType = newValue;
}

GLsizei CC3VertexArray::getElementStride()
{
	return elementStride ? elementStride : GLElementTypeSize(elementType) * elementSize;
}

void CC3VertexArray::setElementStride(GLsizei newValue)
{
	elementStride = newValue;
}

GLuint CC3VertexArray::getBufferID()
{
	return bufferID;
}

void CC3VertexArray::setBufferID(GLuint newValue)
{
	bufferID = newValue;
}

GLenum CC3VertexArray::getBufferTarget()
{
	return GL_ARRAY_BUFFER;
}

GLenum CC3VertexArray::getBufferUsage()
{
	return bufferUsage;
}

void CC3VertexArray::setBufferUsage(GLenum newValue)
{
	bufferUsage = newValue;
}

CC3VertexArray* CC3VertexArray::vertexArray()
{
	CC3VertexArray* pArray = new CC3VertexArray;
	if (pArray) 
	{
		pArray->autorelease();
	}
	
	return pArray;
}

CC3VertexArray* CC3VertexArray::vertexArrayWithTag(GLuint aTag)
{
	CC3VertexArray* pArray = new CC3VertexArray;
	if (pArray) 
	{
		pArray->initWithTag(aTag, pArray->getName().c_str());
		pArray->autorelease();
	}
	
	return pArray;
}

CC3VertexArray* CC3VertexArray::vertexArrayWithName(const char* aName)
{
	CC3VertexArray* pArray = new CC3VertexArray;
	if (pArray) 
	{
		pArray->initWithName(aName);
		pArray->autorelease();
	}
	
	return pArray;
}

CC3VertexArray* CC3VertexArray::vertexArrayWithTag(GLuint aTag, const char* aName)
{
	CC3VertexArray* pArray = new CC3VertexArray;
	if (pArray) 
	{
		pArray->initWithTag(aTag, aName);
		pArray->autorelease();
	}
	
	return pArray;
}

GLvoid* CC3VertexArray::allocateElements(GLsizei elemCount)
{
	if (elemCount) 
	{
		elementCount = elemCount;
		setElements(calloc(elementCount, getElementStride()));	// Safely disposes existing elements
		elementsAreRetained = true;
		//LogTrace(@"%@ allocated space for %u elements", self, elementCount);
	} 
	else 
	{
		deallocateElements();
	}
	
	return elements;
}

bool CC3VertexArray::getShouldAllowVertexBuffering()
{
	return shouldAllowVertexBuffering;
}

void CC3VertexArray::setShouldAllowVertexBuffering(bool newValue)
{
	shouldAllowVertexBuffering = newValue;
}

void CC3VertexArray::createGLBuffer()
{
	if (shouldAllowVertexBuffering && !bufferID) 
	{
		CC3OpenGLES11VertexArrays* gles11Vertices = CC3OpenGLES11Engine::engine()->getVertices();
		CC3OpenGLES11StateTrackerArrayBufferBinding* bufferBinding = gles11Vertices->bufferBinding(getBufferTarget());
		
		//LogTrace(@"%@ creating GL server buffer", self);
		bufferID = gles11Vertices->generateBuffer();
		GLsizeiptr buffSize = getElementStride() * elementCount;
		bufferBinding->setValue(bufferID);
		bufferBinding->loadBufferData(elements, buffSize, bufferUsage);
		GLenum errCode = glGetError();
		if (errCode) 
		{
			//LogInfo(@"Could not create GL buffer of type %@ because of %@. Using app memory arrays.",
//					NSStringFromGLEnum(self.bufferTarget), GetGLErrorText(errCode));
			deleteGLBuffer();
		}
		bufferBinding->unbind();
	} 
	else 
	{
		//LogTrace(@"%@ NOT creating GL server buffer because shouldAllowVertexBuffering is %@ or bufferID is %i",
//				 self, NSStringFromBoolean(shouldAllowVertexBuffering), bufferID);
	}
}

void CC3VertexArray::deleteGLBuffer()
{
	if (bufferID) 
	{
		CC3OpenGLES11Engine::engine()->getVertices()->deleteBuffer(bufferID);
		bufferID = 0;	
	}
}

void CC3VertexArray::updateGLBufferStartingAt(GLuint offsetIndex, GLsizei elemCount)
{
	if (bufferID) 
	{
		CC3OpenGLES11StateTrackerArrayBufferBinding* bufferBinding;
		//LogTrace(@"%@ updating GL server buffer with %i bytes starting at %i", self, length, offset);
		GLsizei elemStride = getElementStride();
		bufferBinding = CC3OpenGLES11Engine::engine()->getVertices()->bufferBinding(getBufferTarget());
		bufferBinding->setValue(bufferID);
		//NSAssert1(elements, @"%@ GL buffer cannot be updated because vertex data has been released", self); 
		bufferBinding->updateBufferData(elements, (offsetIndex * elemStride), (elemCount * elemStride));
	}
}

void CC3VertexArray::updateGLBuffer()
{
	updateGLBufferStartingAt(0, elementCount);
}

bool CC3VertexArray::getShouldReleaseRedundantData()
{
	return shouldReleaseRedundantData;
}

void CC3VertexArray::setShouldReleaseRedundantData(bool newValue)
{
	shouldReleaseRedundantData = newValue;
}

void CC3VertexArray::releaseRedundantData()
{
	if (bufferID && shouldReleaseRedundantData) 
	{
		deallocateElements();
	}
}

void CC3VertexArray::bindWithVisitor(CC3NodeDrawingVisitor* visitor)
{
	if (getSwitchingArray()) 
	{
		bindGLWithVisitor(visitor);
	} 
	else 
	{
		//LogTrace(@"Reusing currently bound %@", self);
	}
}

void CC3VertexArray::bindGLWithVisitor(CC3NodeDrawingVisitor* visitor)
{
	if (bufferID) 
	{											// use GL buffer if it exists
		//LogTrace(@"%@ binding GL buffer containing %u elements", self, elementCount);
		CC3OpenGLES11Engine::engine()->getVertices()->bufferBinding(getBufferTarget())->setValue(bufferID);
		bindPointer((GLvoid*)elementOffset, visitor);
	} 
	else if (elementCount && elements) 
	{					// use local client array if it exists
		//LogTrace(@"%@ using local array containing %u elements", self, elementCount);
		CC3OpenGLES11Engine::engine()->getVertices()->bufferBinding(getBufferTarget())->unbind();
		bindPointer((GLvoid*)((GLuint)elements + elementOffset), visitor);
	} 
	else 
	{
		//LogTrace(@"%@ no elements to bind", self);
	}
}

void CC3VertexArray::bindPointer(GLvoid* pointer, CC3NodeDrawingVisitor* visitor)
{
}

void CC3VertexArray::unbind()
{
	
}

GLvoid* CC3VertexArray::addressOfElement(GLsizei index)
{
	//cocos2d::CCAssert(elements, "Elements are no longer in application memory.");
	return (GLbyte*)elements + (getElementStride() * index) + elementOffset;
}

void CC3VertexArray::resetSwitching()
{
}

bool CC3VertexArray::getSwitchingArray()
{
	return true;
}

bool CC3VertexArray::getElementsAreRetained()
{
	return elementsAreRetained;
}

void CC3VertexArray::setElementsAreRetained(bool newValue)
{
	elementsAreRetained = newValue;
}

void CC3VertexArray::populateFrom(CC3VertexArray* another)
{
	CC3Identifiable::populateFrom(another);
	
	elementType = another->getElementType();
	elementSize = another->getElementSize();
	elementStride = another->getElementStride();
	bufferUsage = another->getBufferUsage();
	elementOffset = another->getElementOffset();
	shouldAllowVertexBuffering = another->getShouldAllowVertexBuffering();
	shouldReleaseRedundantData = another->getShouldReleaseRedundantData();
	
	deleteGLBuffer();		// Data has yet to be buffered. Get rid of old buffer if necessary.
	
	// Allocate memory and copy the vertex data over.
	// Watch out! If this array is part of interleaved data, this will result in multiple copies
	// of the interleaved data, which is probably not what is wanted.
	allocateElements(another->getElementCount());
	memcpy(elements, another->getElements(), elementCount * elementStride);
}

#pragma mark CC3VertexArray

//@interface CC3Identifiable (TemplateMethods)
//-(void) populateFrom: (CC3Identifiable*) another;
//@end
//
//@interface CC3VertexArray (TemplateMethods)
//-(void) bindGLWithVisitor: (CC3NodeDrawingVisitor*) visitor;
//-(void) bindPointer: (GLvoid*) pointer withVisitor: (CC3NodeDrawingVisitor*) visitor;
//@property(nonatomic, readonly) BOOL switchingArray;
//@end
//
//
//@implementation CC3VertexArray
//
//@synthesize elements, elementCount, elementSize, elementType, elementStride;
//@synthesize bufferID, elementOffset, bufferUsage;
//@synthesize shouldAllowVertexBuffering, shouldReleaseRedundantData;
//
//-(void) dealloc {
//	[self deleteGLBuffer];
//	[self deallocateElements];
//	[super dealloc];
//}
//
//-(GLsizei) elementStride {
//	return elementStride ? elementStride : GLElementTypeSize(elementType) * elementSize;
//}
//
//-(GLenum) bufferTarget {
//	return GL_ARRAY_BUFFER;
//}
//
//#pragma mark Allocation and initialization
//
//-(id) initWithTag: (GLuint) aTag withName: (NSString*) aName {
//	if ( (self = [super initWithTag: aTag withName: aName]) ) {
//		elements = nil;
//		elementCount = 0;
//		elementType = GL_FLOAT;
//		elementSize = 3;
//		elementStride = 0;
//		bufferID = 0;
//		bufferUsage = GL_STATIC_DRAW;
//		elementOffset = 0;
//		elementsAreRetained = NO;
//		shouldAllowVertexBuffering = YES;
//		shouldReleaseRedundantData = YES;
//	}
//	return self;
//}
//
//+(id) vertexArray {
//	return [[[self alloc] init] autorelease];
//}
//
//+(id) vertexArrayWithTag: (GLuint) aTag {
//	return [[[self alloc] initWithTag: aTag] autorelease];
//}
//
//+(id) vertexArrayWithName: (NSString*) aName {
//	return [[[self alloc] initWithName: aName] autorelease];
//}
//
//+(id) vertexArrayWithTag: (GLuint) aTag withName: (NSString*) aName {
//	return [[[self alloc] initWithTag: aTag withName: aName] autorelease];
//}
//
//// Template method that populates this instance from the specified other instance.
//// This method is invoked automatically during object copying via the copyWithZone: method.
//-(void) populateFrom: (CC3VertexArray*) another {
//	[super populateFrom: another];
//
//	elementType = another.elementType;
//	elementSize = another.elementSize;
//	elementStride = another.elementStride;
//	bufferUsage = another.bufferUsage;
//	elementOffset = another.elementOffset;
//	shouldAllowVertexBuffering = another.shouldAllowVertexBuffering;
//	shouldReleaseRedundantData = another.shouldReleaseRedundantData;
//
//	[self deleteGLBuffer];		// Data has yet to be buffered. Get rid of old buffer if necessary.
//
//	// Allocate memory and copy the vertex data over.
//	// Watch out! If this array is part of interleaved data, this will result in multiple copies
//	// of the interleaved data, which is probably not what is wanted.
//	[self allocateElements: another.elementCount];
//	memcpy(elements, another.elements, elementCount * elementStride);
//}
//
//
//#pragma mark Tag allocation
//
//// Class variable tracking the most recent tag value assigned for CC3VertexArrays.
//// This class variable is automatically incremented whenever the method nextTag is called.
//static GLuint lastAssignedVertexArrayTag;
//
//-(GLuint) nextTag {
//	return ++lastAssignedVertexArrayTag;
//}
//
//+(void) resetTagAllocation {
//	lastAssignedVertexArrayTag = 0;
//}
//
//
//#pragma mark Binding GL artifacts
//
//-(GLvoid*) allocateElements: (GLsizei) elemCount {
//	if (elemCount) {
//		elementCount = elemCount;
//		self.elements = calloc(elementCount, self.elementStride);	// Safely disposes existing elements
//		elementsAreRetained = YES;
//		LogTrace(@"%@ allocated space for %u elements", self, elementCount);
//	} else {
//		[self deallocateElements];
//	}
//	return elements;
//}
//
//// Does not change elementCount, because that is used for drawing.
//-(void) deallocateElements {
//	if (elementsAreRetained && elements) {
//		free(elements);
//		elements = NULL;
//		elementsAreRetained = NO;
//		LogTrace(@"%@ deallocated %u previously allocated elements", self, elementCount);
//	}
//}
//
//-(void) setElements: (GLvoid*) elems {
//	[self deallocateElements];		// Safely disposes existing elements
//	elements = elems;
//}
//
//-(void) createGLBuffer {
//	if (shouldAllowVertexBuffering && !bufferID) {
//		CC3OpenGLES11VertexArrays* gles11Vertices = [CC3OpenGLES11Engine engine].vertices;
//		CC3OpenGLES11StateTrackerArrayBufferBinding* bufferBinding = [gles11Vertices bufferBinding: self.bufferTarget];
//
//		LogTrace(@"%@ creating GL server buffer", self);
//		bufferID =[gles11Vertices generateBuffer];
//		GLsizeiptr buffSize = self.elementStride * elementCount;
//		bufferBinding.value = bufferID;
//		[bufferBinding loadBufferData: elements ofLength: buffSize forUse: bufferUsage];
//		GLenum errCode = glGetError();
//		if (errCode) {
//			LogInfo(@"Could not create GL buffer of type %@ because of %@. Using app memory arrays.",
//					NSStringFromGLEnum(self.bufferTarget), GetGLErrorText(errCode));
//			[self deleteGLBuffer];
//		}
//		[bufferBinding unbind];
//	} else {
//		LogTrace(@"%@ NOT creating GL server buffer because shouldAllowVertexBuffering is %@ or bufferID is %i",
//				 self, NSStringFromBoolean(shouldAllowVertexBuffering), bufferID);
//	}
//}
//
//-(void) updateGLBufferStartingAt: (GLuint) offsetIndex forLength: (GLsizei) elemCount {
//	if (bufferID) {
//		CC3OpenGLES11StateTrackerArrayBufferBinding* bufferBinding;
//		LogTrace(@"%@ updating GL server buffer with %i bytes starting at %i", self, length, offset);
//		GLsizei elemStride = self.elementStride;
//		bufferBinding = [[CC3OpenGLES11Engine engine].vertices bufferBinding: self.bufferTarget];
//		bufferBinding.value = bufferID;
//		NSAssert1(elements, @"%@ GL buffer cannot be updated because vertex data has been released", self); 
//		[bufferBinding updateBufferData: elements
//							 startingAt: (offsetIndex * elemStride)
//							  forLength: (elemCount * elemStride)];
//	}
//}
//
//-(void) updateGLBuffer {
//	[self updateGLBufferStartingAt: 0 forLength: elementCount];
//}
//
//-(void) releaseRedundantData {
//	if (bufferID && shouldReleaseRedundantData) {
//		[self deallocateElements];
//	}
//}
//
//-(void) deleteGLBuffer {
//	if (bufferID) {
//		[[CC3OpenGLES11Engine engine].vertices deleteBuffer: bufferID];
//		bufferID = 0;	
//	}
//}
//
//-(void) bindWithVisitor: (CC3NodeDrawingVisitor*) visitor {
//	if (self.switchingArray) {
//		[self bindGLWithVisitor: visitor];
//	} else {
//		LogTrace(@"Reusing currently bound %@", self);
//	}
//}
//
///**
// * Template method that binds the GL engine to the underlying vertex data,
// * in preparation for drawing.
// *
// * If the data has been copied into a VBO in GL memory, binds the GL engine to the bufferID
// * property, and invokes bindPointer: with the value of the elementOffset property.
// * If a VBO is not used, unbinds the GL from any VBO's, and invokes bindPointer: with a pointer
// * to the first data element managed by this vertex array instance.
// */
//-(void) bindGLWithVisitor: (CC3NodeDrawingVisitor*) visitor {
//	if (bufferID) {											// use GL buffer if it exists
//		LogTrace(@"%@ binding GL buffer containing %u elements", self, elementCount);
//		[[CC3OpenGLES11Engine engine].vertices bufferBinding: self.bufferTarget].value = bufferID;
//		[self bindPointer: (GLvoid*)elementOffset withVisitor: visitor];
//	} else if (elementCount && elements) {					// use local client array if it exists
//		LogTrace(@"%@ using local array containing %u elements", self, elementCount);
//		[[[CC3OpenGLES11Engine engine].vertices bufferBinding: self.bufferTarget] unbind];
//		[self bindPointer: (GLvoid*)((GLuint)elements + elementOffset) withVisitor: visitor];
//	} else {
//		LogTrace(@"%@ no elements to bind", self);
//	}
//}
//
///**
// * Template method that binds the GL engine to the values of the elementSize, elementType
// * and elementStride properties, along with the specified data pointer, and enables the
// * type of aspect managed by this instance (locations, normals...) in the GL engine.
// *
// * This abstract implementation does nothing. Subclasses will override to handle
// * their particular type of vetex aspect.
// */
//-(void) bindPointer: (GLvoid*) pointer withVisitor: (CC3NodeDrawingVisitor*) visitor {}
//
//-(void) unbind {
//	[[self class] unbind];
//}
//
//// Default does nothing. Subclasses will override.
//+(void) unbind {}
//
//
//#pragma mark Accessing elements
//
//-(GLvoid*) addressOfElement: (GLsizei) index {
//	NSAssert(elements, @"Elements are no longer in application memory.");
//	return (GLbyte*)elements + (self.elementStride * index) + elementOffset;
//}
//
//
//#pragma mark Array context switching
//
///**
// * Returns whether this vertex array is different than the vertex array of the same type
// * that was most recently bound to the GL engine. To improve performance, vertex arrays
// * are only bound if they need to be.
// *
// * If appropriate, the application can arrange CC3MeshNodes in the CC3World so that nodes
// * using the same vertex arrays are drawn together, to minimize the number of binding
// * changes in the GL engine.
// */
//-(BOOL) switchingArray {
//	return YES;
//}
//
//+(void) resetSwitching {}
//
//@end


#pragma mark -
#pragma mark CC3DrawableVertexArray

CC3DrawableVertexArray::CC3DrawableVertexArray() :
	firstElement(0),
	stripLengthsAreRetained(false)
{
}

CC3DrawableVertexArray::~CC3DrawableVertexArray()
{
	deallocateStripLengths();
}

void CC3DrawableVertexArray::deallocateStripLengths()
{
	if (stripLengthsAreRetained) 
	{
		free(stripLengths);
		stripLengthsAreRetained = false;
	}
	stripLengths = NULL;
	stripCount = 0;
}

bool CC3DrawableVertexArray::initWithTag(GLuint aTag, const char* aName)
{
	if ( CC3VertexArray::initWithTag(aTag, aName) ) 
	{
		drawingMode = GL_TRIANGLE_STRIP;
		stripCount = 0;
		stripLengths = NULL;
		return true;
	}
	
	return false;
}

void CC3DrawableVertexArray::setDrawingMode(GLenum newValue)
{
	drawingMode = newValue;
}

GLenum CC3DrawableVertexArray::getDrawingMode()
{
	return drawingMode;
}

GLuint CC3DrawableVertexArray::getStripCount()
{
	return stripCount;
}

void CC3DrawableVertexArray::setStripCount(GLuint newValue)
{
	stripCount = newValue;
}

GLuint* CC3DrawableVertexArray::getStripLengths()
{
	return stripLengths;
}

void CC3DrawableVertexArray::setStripLengths(GLuint* newValue)
{
	stripLengths = newValue;
}

GLuint CC3DrawableVertexArray::getFirstElement()
{
	return 0;
}

void CC3DrawableVertexArray::drawWithVisitor(CC3NodeDrawingVisitor* visitor)
{
	GLuint startOfStrip = getFirstElement();
	if (stripCount) 
	{
		//LogTrace(@"%@ drawing %u strips", self, stripCount);
		for (unsigned int i=0; i < stripCount; i++) 
		{
			GLuint stripLen = stripLengths[i];
			drawStripOfLength(stripLen, startOfStrip);
			visitor->getPerformanceStatistics()->addSingleCallFacesPresented(getFaceCountFromVertexCount(stripLen));
			startOfStrip += stripLen;
		}
	} 
	else 
	{
		drawStripOfLength(elementCount, startOfStrip);
		if (visitor->getPerformanceStatistics()) 
		{
			visitor->getPerformanceStatistics()->addSingleCallFacesPresented(getFaceCountFromVertexCount(elementCount));
		}
	}
}

void CC3DrawableVertexArray::drawStripOfLength(GLuint stripLen, GLuint startOfStrip)
{
}

GLsizei CC3DrawableVertexArray::getFaceCountFromVertexCount(GLsizei vc)
{
	switch (getDrawingMode()) 
	{
		case GL_TRIANGLES:
			return vc / 3;
		case GL_TRIANGLE_STRIP:
		case GL_TRIANGLE_FAN:
			return vc - 2;
		case GL_LINES:
			return vc / 2;
		case GL_LINE_STRIP:
			return vc - 1;
		case GL_LINE_LOOP:
		case GL_POINTS:
			return vc;
		default:
			//LogError(@"%@ encountered unknown drawing mode %u", self, self.drawingMode);
			return vc;
	}
}

GLsizei CC3DrawableVertexArray::getVertexCountFromFaceCount(GLsizei fc)
{
	switch (getDrawingMode()) 
	{
		case GL_TRIANGLES:
			return fc * 3;
		case GL_TRIANGLE_STRIP:
		case GL_TRIANGLE_FAN:
			return fc + 2;
		case GL_LINES:
			return fc * 2;
		case GL_LINE_STRIP:
			return fc + 1;
		case GL_LINE_LOOP:
		case GL_POINTS:
			return fc;
		default:
			//LogError(@"%@ encountered unknown drawing mode %u", self, self.drawingMode);
			return fc;
	}
}

bool CC3DrawableVertexArray::getStripLengthsAreRetained()
{
	return stripLengthsAreRetained;
}

void CC3DrawableVertexArray::setStripLengthsAreRetained(bool newValue)
{
	stripLengthsAreRetained = newValue;
}

void CC3DrawableVertexArray::populateFrom(CC3DrawableVertexArray* another)
{
	CC3VertexArray::populateFrom(another);
	
	drawingMode = another->getDrawingMode();
	
	// Allocate memory for strips, then copy them over
	allocateStripLengths(another->getStripCount());
	GLuint* otherStripLengths = another->getStripLengths();
	for(int i=0; i < stripCount; i++) 
	{
		stripLengths[i] = otherStripLengths[i];
	}
}

void CC3DrawableVertexArray::allocateStripLengths(GLsizei sCount)
{
	deallocateStripLengths();			// get rid of any existing array
	
	if (sCount) 
	{
		stripCount = sCount;
		stripLengths = (GLuint*)calloc(stripCount, sizeof(GLuint));
		stripLengthsAreRetained = true;
	}
}

//@interface CC3DrawableVertexArray (TemplateArray)
//-(void) drawStripOfLength: (GLuint) stripLen startingAt: (GLuint) startOfStrip;
//-(GLsizei) faceCountFromVertexCount: (GLsizei) vc;
//-(GLsizei) vertexCountFromFaceCount: (GLsizei) fc;
//@end
//
//@implementation CC3DrawableVertexArray
//
//@synthesize drawingMode, stripCount, stripLengths;
//
//-(void) dealloc {
//	[self deallocateStripLengths];
//	[super dealloc];
//}
//
//-(GLuint) firstElement {
//	return 0;
//}
//
//-(id) initWithTag: (GLuint) aTag withName: (NSString*) aName {
//	if ( (self = [super initWithTag: aTag withName: aName]) ) {
//		drawingMode = GL_TRIANGLE_STRIP;
//		stripCount = 0;
//		stripLengths = NULL;
//	}
//	return self;
//}
//
//// Template method that populates this instance from the specified other instance.
//// This method is invoked automatically during object copying via the copyWithZone: method.
//-(void) populateFrom: (CC3DrawableVertexArray*) another {
//	[super populateFrom: another];
//
//	drawingMode = another.drawingMode;
//
//	// Allocate memory for strips, then copy them over
//	[self allocateStripLengths: another.stripCount];
//	GLuint* otherStripLengths = another.stripLengths;
//	for(int i=0; i < stripCount; i++) {
//		stripLengths[i] = otherStripLengths[i];
//	}
//}
//
//-(void) drawWithVisitor: (CC3NodeDrawingVisitor*) visitor {
//	GLuint startOfStrip = self.firstElement;
//	if (stripCount) {
//		LogTrace(@"%@ drawing %u strips", self, stripCount);
//		for (uint i=0; i < stripCount; i++) {
//			GLuint stripLen = stripLengths[i];
//			[self drawStripOfLength: stripLen startingAt: startOfStrip];
//			[visitor.performanceStatistics addSingleCallFacesPresented: [self faceCountFromVertexCount: stripLen]];
//			startOfStrip += stripLen;
//		}
//	} else {
//		[self drawStripOfLength: elementCount startingAt: startOfStrip];
//		[visitor.performanceStatistics addSingleCallFacesPresented: [self faceCountFromVertexCount: elementCount]];
//	}
//}
//
//
///**
// * Draws a single strip of vertices, of the specified number of elements, starting at
// * the element at startOfStrip.
// *
// * If drawing is to be performed in a single GL call, this method can be invoked
// * with stripLen equal to the elementCount property, and startOfStrip equal to the
// * firstElement property.
// *
// * This abstract implementation does nothing. Subclasses will override.
// */
//-(void) drawStripOfLength: (GLuint) stripLen startingAt: (GLuint) startOfStrip {} 
//
//-(void) allocateStripLengths: (GLsizei) sCount {
//	[self deallocateStripLengths];			// get rid of any existing array
//	
//	if (sCount) {
//		stripCount = sCount;
//		stripLengths = calloc(stripCount, sizeof(GLuint));
//		stripLengthsAreRetained = YES;
//	}
//}
//
//-(void) deallocateStripLengths {
//	if (stripLengthsAreRetained) {
//		free(stripLengths);
//		stripLengthsAreRetained = NO;
//	}
//	stripLengths = nil;
//	stripCount = 0;
//}
//
///** Converts the specified vertex count to a face count, based on the drawingMode property. */
//-(GLsizei) faceCountFromVertexCount: (GLsizei) vc {
//	switch (self.drawingMode) {
//		case GL_TRIANGLES:
//			return vc / 3;
//		case GL_TRIANGLE_STRIP:
//		case GL_TRIANGLE_FAN:
//			return vc - 2;
//		case GL_LINES:
//			return vc / 2;
//		case GL_LINE_STRIP:
//			return vc - 1;
//		case GL_LINE_LOOP:
//		case GL_POINTS:
//			return vc;
//		default:
//			LogError(@"%@ encountered unknown drawing mode %u", self, self.drawingMode);
//			return vc;
//	}
//}
//
///** Converts the specified face count to a vertex count, based on the drawingMode property. */
//-(GLsizei) vertexCountFromFaceCount: (GLsizei) fc {
//	switch (self.drawingMode) {
//		case GL_TRIANGLES:
//			return fc * 3;
//		case GL_TRIANGLE_STRIP:
//		case GL_TRIANGLE_FAN:
//			return fc + 2;
//		case GL_LINES:
//			return fc * 2;
//		case GL_LINE_STRIP:
//			return fc + 1;
//		case GL_LINE_LOOP:
//		case GL_POINTS:
//			return fc;
//		default:
//			LogError(@"%@ encountered unknown drawing mode %u", self, self.drawingMode);
//			return fc;
//	}
//}
//
//@end


#pragma mark -
#pragma mark CC3VertexLocations

CC3VertexLocations::CC3VertexLocations()
{
	firstElement = 0;
	centerOfGeometry = kCC3VectorZero;
	boundingBox.minimum = kCC3VectorZero;
	boundingBox.maximum = kCC3VectorZero;
	setBoundingBoxNeedsBuilding(true);
}

CC3VertexLocations::~CC3VertexLocations()
{
}

CC3VertexLocations* CC3VertexLocations::vertexArrayWithName(const char* aName)
{
	CC3VertexLocations* pArray = new CC3VertexLocations;
	if (pArray) 
	{
		pArray->initWithName(aName);
		pArray->autorelease();
	}
	
	return pArray;
}

GLuint CC3VertexLocations::getFirstElement()
{
	return firstElement;
}

void CC3VertexLocations::setFirstElement(GLuint newValue)
{
	firstElement = newValue;
}

bool CC3VertexLocations::getBoundingBoxNeedsBuilding()
{
	return boundingBoxNeedsBuilding;
}

void CC3VertexLocations::setBoundingBoxNeedsBuilding(bool newValue)
{
	boundingBoxNeedsBuilding = newValue;
}

CC3BoundingBox CC3VertexLocations::getBoundingBox()
{
	buildBoundingBoxIfNecessary();
	return boundingBox;
}

void CC3VertexLocations::setBoundingBox(CC3BoundingBox newValue)
{
	boundingBox = newValue;
}

CC3Vector CC3VertexLocations::getCenterOfGeometry()
{
	buildBoundingBoxIfNecessary();
	return centerOfGeometry;
}

void CC3VertexLocations::setCenterOfGeometry(CC3Vector newValue)
{
	centerOfGeometry = newValue;
}

CC3Vector CC3VertexLocations::locationAt(GLsizei index)
{
	return *(CC3Vector*)(addressOfElement(index));
}

void CC3VertexLocations::setLocation(CC3Vector aLocation, GLsizei index)
{
	*(CC3Vector*)(addressOfElement(index)) = aLocation;
	setBoundingBoxNeedsBuilding(true);
}

void CC3VertexLocations::movePivotTo(CC3Vector aLocation)
{
	for (GLsizei i = 0; i < elementCount; i++) 
	{
		CC3Vector locOld = locationAt(i);
		CC3Vector locNew = CC3VectorDifference(locOld, aLocation);
		setLocation(locNew, i);
	}
	
	setBoundingBoxNeedsBuilding(true);
}

void CC3VertexLocations::movePivotToCenterOfGeometry()
{
	movePivotTo(getCenterOfGeometry());
}

void CC3VertexLocations::setElements(GLvoid* elems)
{
	CC3DrawableVertexArray::setElements(elems);
	setBoundingBoxNeedsBuilding(true);
}

void CC3VertexLocations::buildBoundingBox()
{
	//cocos2d::CCAssert(elements, "%d bounding box requested after elements data have been released", (int)this);
	//cocos2d::CCAssert(elementType == GL_FLOAT, "%d must have elementType GLFLOAT to build the bounding box", (int)this);
	
	CC3Vector vl, vlMin, vlMax;
	vl = locationAt(0);
	vlMin = vl;
	vlMax = vl;
	for (GLsizei i = 1; i < elementCount; i++) 
	{
		vl = locationAt(i);
		vlMin = CC3VectorMinimize(vlMin, vl);
		vlMax = CC3VectorMaximize(vlMax, vl);
	}
	
	getBoundingBox().minimum = vlMin;
	getBoundingBox().maximum = vlMax;
	centerOfGeometry = CC3VectorScaleUniform(CC3VectorAdd(vlMax, vlMin), 0.5);
	setBoundingBoxNeedsBuilding(false);
	//LogTrace(@"%@ bounding box: (%@, %@) and center of geometry: %@", self,
//			 NSStringFromCC3Vector(boundingBox.minimum),
//			 NSStringFromCC3Vector(boundingBox.maximum),
//			 NSStringFromCC3Vector(centerOfGeometry));
}

void CC3VertexLocations::buildBoundingBoxIfNecessary()
{
	if (boundingBoxNeedsBuilding) 
	{
		buildBoundingBox();
	}
}

void CC3VertexLocations::releaseRedundantData()
{
	buildBoundingBoxIfNecessary();
	CC3DrawableVertexArray::releaseRedundantData();
}

void CC3VertexLocations::bindPointer(GLvoid* pointer, CC3NodeDrawingVisitor* visitor)
{
	CC3OpenGLES11Engine::engine()->getVertices()->getLocations()->useElementsAt(pointer,
																				 elementSize,
																				 elementType,
																				 elementStride);
	CC3OpenGLES11Engine::engine()->getClientCapabilities()->getVertexArray()->enable();
}

void CC3VertexLocations::unbind()
{
	CC3OpenGLES11Engine::engine()->getClientCapabilities()->getVertexArray()->disable();
	resetSwitching();
}

void CC3VertexLocations::drawStripOfLength(GLuint stripLen, GLuint startOfStrip)
{
	//LogTrace(@"%@ drawing %u vertices", self, stripLen);
	CC3OpenGLES11Engine::engine()->getVertices()->drawVerticiesAs(drawingMode,
																   startOfStrip,
																   stripLen);
}

static GLuint currentLocationsTag = 0;

void CC3VertexLocations::resetSwitching()
{
	currentLocationsTag = 0;
}

bool CC3VertexLocations::switchingArray()
{
	bool shouldSwitch = currentLocationsTag != tag;
	currentLocationsTag = tag;		// Set anyway - either it changes or it doesn't.
	return shouldSwitch;
}

//@interface CC3VertexLocations (TemplateMethods)
//-(void) buildBoundingBox;
//-(void) buildBoundingBoxIfNecessary;
//@end
//
//
//@implementation CC3VertexLocations
//
//@synthesize firstElement, boundingBoxNeedsBuilding;
//
//-(void) setElements: (GLvoid*) elems {
//	[super setElements: elems];
//	self.boundingBoxNeedsBuilding = YES;
//}
//
//-(id) init {
//	if ( (self = [super init]) ) {
//		firstElement = 0;
//		centerOfGeometry = kCC3VectorZero;
//		boundingBox.minimum = kCC3VectorZero;
//		boundingBox.maximum = kCC3VectorZero;
//		self.boundingBoxNeedsBuilding = YES;
//	}
//	return self;
//}
//
//// Template method that populates this instance from the specified other instance.
//// This method is invoked automatically during object copying via the copyWithZone: method.
//-(void) populateFrom: (CC3VertexLocations*) another {
//	[super populateFrom: another];
//
//	firstElement = another.firstElement;
//	boundingBox = another.boundingBox;
//	centerOfGeometry = another.centerOfGeometry;
//	boundingBoxNeedsBuilding = another.boundingBoxNeedsBuilding;
//}
//
//-(CC3Vector) locationAt: (GLsizei) index {
//	return *(CC3Vector*)[self addressOfElement: index];
//}
//
//-(void) setLocation: (CC3Vector) aLocation at: (GLsizei) index {
//	*(CC3Vector*)[self addressOfElement: index] = aLocation;
//	self.boundingBoxNeedsBuilding = YES;
//}
//
//
///** Returns the boundingBox, building it if necessary. */
//-(CC3BoundingBox) boundingBox {
//	[self buildBoundingBoxIfNecessary];
//	return boundingBox;
//}
//
///** Returns the centerOfGeometry, calculating it via the bounding box if necessary. */
//-(CC3Vector) centerOfGeometry {
//	[self buildBoundingBoxIfNecessary];
//	return centerOfGeometry;
//}
//
///** Builds the bounding box if it needs to be built. */
//-(void) buildBoundingBoxIfNecessary {
//	if (boundingBoxNeedsBuilding) {
//		[self buildBoundingBox];
//	}
//}
//
///**
// * Calculates and populates the boundingBox and centerOfGeometry properties
// * from the vertex locations.
// *
// * This method is invoked automatically when the bounding box or centerOfGeometry property
// * is accessed for the first time after the elements property has been set.
// */
//-(void) buildBoundingBox {
//	NSAssert1(elements, @"%@ bounding box requested after elements data have been released", self);
//	NSAssert1(elementType == GL_FLOAT, @"%@ must have elementType GLFLOAT to build the bounding box", self);
//
//	CC3Vector vl, vlMin, vlMax;
//	vl = [self locationAt: 0];
//	vlMin = vl;
//	vlMax = vl;
//	for (GLsizei i = 1; i < elementCount; i++) {
//		vl = [self locationAt: i];
//		vlMin = CC3VectorMinimize(vlMin, vl);
//		vlMax = CC3VectorMaximize(vlMax, vl);
//	}
//	boundingBox.minimum = vlMin;
//	boundingBox.maximum = vlMax;
//	centerOfGeometry = CC3VectorScaleUniform(CC3VectorAdd(vlMax, vlMin), 0.5);
//	self.boundingBoxNeedsBuilding = NO;
//	LogTrace(@"%@ bounding box: (%@, %@) and center of geometry: %@", self,
//			 NSStringFromCC3Vector(boundingBox.minimum),
//			 NSStringFromCC3Vector(boundingBox.maximum),
//			 NSStringFromCC3Vector(centerOfGeometry));
//}
//
//-(void) movePivotTo: (CC3Vector) aLocation {
//	for (GLsizei i = 0; i < elementCount; i++) {
//		CC3Vector locOld = [self locationAt: i];
//		CC3Vector locNew = CC3VectorDifference(locOld, aLocation);
//		[self setLocation: locNew at: i];
//	}
//	self.boundingBoxNeedsBuilding = YES;
//}
//
//-(void) movePivotToCenterOfGeometry {
//	[self movePivotTo: self.centerOfGeometry];
//}
//
//
//#pragma mark Binding GL artifacts
//
///** Overridden to ensure the bounding box is built before releasing the vertices. */
//-(void) releaseRedundantData {
//	[self buildBoundingBoxIfNecessary];
//	[super releaseRedundantData];
//}
//
//-(void) bindPointer: (GLvoid*) pointer withVisitor: (CC3NodeDrawingVisitor*) visitor {
//	[[CC3OpenGLES11Engine engine].vertices.locations useElementsAt: pointer
//														  withSize: elementSize
//														  withType: elementType
//														withStride: elementStride];
//	[[CC3OpenGLES11Engine engine].clientCapabilities.vertexArray enable];
//}
//
//+(void) unbind {
//	[[CC3OpenGLES11Engine engine].clientCapabilities.vertexArray disable];
//	[self resetSwitching];
//}
//
//-(void) drawStripOfLength: (GLuint) stripLen startingAt: (GLuint) startOfStrip {
//	LogTrace(@"%@ drawing %u vertices", self, stripLen);
//	[[CC3OpenGLES11Engine engine].vertices drawVerticiesAs: drawingMode
//												startingAt: startOfStrip
//												withLength: stripLen];
//} 
//
//
//#pragma mark Array context switching
//
//// The tag of the array that was most recently drawn to the GL engine.
//// The GL engine is only updated when an array of the same type with a different tag is presented.
//// This allows for optimization by ordering the drawing of objects so that objects with
//// the same arrays are drawn together, to minimize context switching within the GL engine.
//static GLuint currentLocationsTag = 0;
//
//-(BOOL) switchingArray {
//	BOOL shouldSwitch = currentLocationsTag != tag;
//	currentLocationsTag = tag;		// Set anyway - either it changes or it doesn't.
//	return shouldSwitch;
//}
//
//+(void) resetSwitching {
//	currentLocationsTag = 0;
//}
//
//@end


#pragma mark -
#pragma mark CC3VertexNormals

CC3VertexNormals::CC3VertexNormals()
{
}

CC3VertexNormals::~CC3VertexNormals()
{
}

CC3VertexNormals* CC3VertexNormals::vertexArrayWithName(const char* aName)
{
	CC3VertexNormals* pArray = new CC3VertexNormals;
	if (pArray) 
	{
		pArray->initWithName(aName);
		pArray->autorelease();
	}
	
	return pArray;
}

CC3Vector CC3VertexNormals::normalAt(GLsizei index)
{
	return *(CC3Vector*)addressOfElement(index);
}

void CC3VertexNormals::setNormal(CC3Vector aNormal, GLsizei index)
{
	*(CC3Vector*)addressOfElement(index) = aNormal;
}

void CC3VertexNormals::bindPointer(GLvoid* pointer, CC3NodeDrawingVisitor* visitor)
{
	CC3OpenGLES11Engine::engine()->getVertices()->getNormals()->useElementsAt(pointer,
																				elementType,
																				elementStride);
	CC3OpenGLES11Engine::engine()->getClientCapabilities()->getNormalArray()->enable();
}

void CC3VertexNormals::unbind()
{
	CC3OpenGLES11Engine::engine()->getClientCapabilities()->getNormalArray()->disable();
	resetSwitching();
}

static GLuint currentNormalsTag = 0;

bool CC3VertexNormals::switchingArray()
{
	bool shouldSwitch = currentNormalsTag != tag;
	currentNormalsTag = tag;		// Set anyway - either it changes or it doesn't.
	return shouldSwitch;
}

void CC3VertexNormals::resetSwitching()
{
	currentNormalsTag = 0;
}

//@implementation CC3VertexNormals
//
//-(CC3Vector) normalAt: (GLsizei) index {
//	return *(CC3Vector*)[self addressOfElement: index];
//}
//
//-(void) setNormal: (CC3Vector) aNormal at: (GLsizei) index {
//	*(CC3Vector*)[self addressOfElement: index] = aNormal;
//}
//
//-(void) bindPointer: (GLvoid*) pointer withVisitor: (CC3NodeDrawingVisitor*) visitor {
//	[[CC3OpenGLES11Engine engine].vertices.normals useElementsAt: pointer
//														withType: elementType
//													  withStride: elementStride];
//	[[CC3OpenGLES11Engine engine].clientCapabilities.normalArray enable];
//}
//
//+(void) unbind {
//	[[CC3OpenGLES11Engine engine].clientCapabilities.normalArray disable];
//	[self resetSwitching];
//}
//
//
//#pragma mark Array context switching
//
//// The tag of the array that was most recently drawn to the GL engine.
//// The GL engine is only updated when an array of the same type with a different tag is presented.
//// This allows for optimization by ordering the drawing of objects so that objects with
//// the same arrays are drawn together, to minimize context switching within the GL engine.
//static GLuint currentNormalsTag = 0;
//
//-(BOOL) switchingArray {
//	BOOL shouldSwitch = currentNormalsTag != tag;
//	currentNormalsTag = tag;		// Set anyway - either it changes or it doesn't.
//	return shouldSwitch;
//}
//
//+(void) resetSwitching {
//	currentNormalsTag = 0;
//}
//
//@end


#pragma mark -
#pragma mark CC3VertexColors

CC3VertexColors::CC3VertexColors()
{
}

CC3VertexColors::~CC3VertexColors()
{
}

cocos2d::ccColor4F CC3VertexColors::color4FAt(GLsizei index)
{
	return *(cocos2d::ccColor4F*)addressOfElement(index);
}

void CC3VertexColors::setColor4F(cocos2d::ccColor4F aColor, GLsizei index)
{
	*(cocos2d::ccColor4F*)addressOfElement(index) = aColor;
}

cocos2d::ccColor4B CC3VertexColors::color4BAt(GLsizei index)
{
	return *(cocos2d::ccColor4B*)addressOfElement(index);
}

void CC3VertexColors::setColor4B(cocos2d::ccColor4B aColor, GLsizei index)
{
	*(cocos2d::ccColor4B*)addressOfElement(index) = aColor;
}

bool CC3VertexColors::initWithTag(GLuint aTag, const char* aName)
{
	if ( CC3VertexArray::initWithTag(aTag, aName) ) 
	{
		elementSize = 4;
		return true;
	}
	
	return false;
}

void CC3VertexColors::bindPointer(GLvoid* pointer, CC3NodeDrawingVisitor* visitor)
{
	CC3OpenGLES11Engine* gles11Engine = CC3OpenGLES11Engine::engine();
	gles11Engine->getVertices()->getColors()->useElementsAt(pointer,
															elementSize,
															elementType,
															elementStride);
	gles11Engine->getClientCapabilities()->getColorArray()->enable();
	
	// Since material color tracking mucks with both ambient and diffuse material colors under
	// the covers, we won't really know what the ambient and diffuse material color values will
	// be when we get back to setting them...so indicate that to the corresponding trackers.
	gles11Engine->getMaterials()->getAmbientColor()->setValueIsKnown(false);
	gles11Engine->getMaterials()->getDiffuseColor()->setValueIsKnown(false);
}

void CC3VertexColors::unbind()
{
	CC3OpenGLES11Engine::engine()->getClientCapabilities()->getColorArray()->disable();
	CC3VertexColors::resetSwitching();
}

static GLuint currentColorsTag = 0;

bool CC3VertexColors::switchingArray()
{
	bool shouldSwitch = currentColorsTag != tag;
	currentColorsTag = tag;		// Set anyway - either it changes or it doesn't.
	return shouldSwitch;
}

void CC3VertexColors::resetSwitching() 
{
	currentColorsTag = 0;
}


//@implementation CC3VertexColors
//
//-(id) initWithTag: (GLuint) aTag withName: (NSString*) aName {
//	if ( (self = [super initWithTag: aTag withName: aName]) ) {
//		elementSize = 4;
//	}
//	return self;
//}
//
//-(ccColor4F) color4FAt: (GLsizei) index {
//	return *(ccColor4F*)[self addressOfElement: index];
//}
//
//-(void) setColor4F: (ccColor4F) aColor at: (GLsizei) index {
//	*(ccColor4F*)[self addressOfElement: index] = aColor;
//}
//
//-(ccColor4B) color4BAt: (GLsizei) index {
//	return *(ccColor4B*)[self addressOfElement: index];
//}
//
//-(void) setColor4B: (ccColor4B) aColor at: (GLsizei) index {
//	*(ccColor4B*)[self addressOfElement: index] = aColor;
//}
//
//-(void) bindPointer: (GLvoid*) pointer withVisitor: (CC3NodeDrawingVisitor*) visitor {
//	CC3OpenGLES11Engine* gles11Engine = [CC3OpenGLES11Engine engine];
//	[gles11Engine.vertices.colors useElementsAt: pointer
//									   withSize: elementSize
//									   withType: elementType
//									 withStride: elementStride];
//	[gles11Engine.clientCapabilities.colorArray enable];
//
//	// Since material color tracking mucks with both ambient and diffuse material colors under
//	// the covers, we won't really know what the ambient and diffuse material color values will
//	// be when we get back to setting them...so indicate that to the corresponding trackers.
//	gles11Engine.materials.ambientColor.valueIsKnown = NO;
//	gles11Engine.materials.diffuseColor.valueIsKnown = NO;
//}
//
//+(void) unbind {
//	[[CC3OpenGLES11Engine engine].clientCapabilities.colorArray disable];
//	[self resetSwitching];
//}
//
//
//#pragma mark Array context switching
//
//// The tag of the array that was most recently drawn to the GL engine.
//// The GL engine is only updated when an array of the same type with a different tag is presented.
//// This allows for optimization by ordering the drawing of objects so that objects with
//// the same arrays are drawn together, to minimize context switching within the GL engine.
//static GLuint currentColorsTag = 0;
//
//-(BOOL) switchingArray {
//	BOOL shouldSwitch = currentColorsTag != tag;
//	currentColorsTag = tag;		// Set anyway - either it changes or it doesn't.
//	return shouldSwitch;
//}
//
//+(void) resetSwitching {
//	currentColorsTag = 0;
//}
//
//@end


#pragma mark -
#pragma mark CC3VertexTextureCoordinates

CC3VertexTextureCoordinates::CC3VertexTextureCoordinates()
{
}

CC3VertexTextureCoordinates::~CC3VertexTextureCoordinates()
{
}

CC3VertexTextureCoordinates* CC3VertexTextureCoordinates::vertexArrayWithName(const char* aName)
{
	CC3VertexTextureCoordinates* pArray = new CC3VertexTextureCoordinates;
	if (pArray) 
	{
		pArray->initWithName(aName);
		pArray->autorelease();
	}
	
	return pArray;
}

cocos2d::CCRect CC3VertexTextureCoordinates::getTextureRectangle()
{
	return textureRectangle;
}

void CC3VertexTextureCoordinates::setTextureRectangle(cocos2d::CCRect aRect)
{
	cocos2d::CCRect oldRect = textureRectangle;
	textureRectangle = aRect;
	alignWithTextureRectangle(aRect, oldRect);
}

void CC3VertexTextureCoordinates::alignWithTextureRectangle(cocos2d::CCRect newRect, cocos2d::CCRect oldRect)
{
	// The size of the mapping in its natural state, before slicing out a rectangle 
	GLfloat natWidth = getNaturalMapSize().width;
	GLfloat natHeight = getNaturalMapSize().height;
	
	// Origin of new rect
	GLfloat nx = newRect.origin.x;
	GLfloat ny = newRect.origin.y;
	
	// Origin of old rect
	GLfloat ox = oldRect.origin.x;
	GLfloat oy = oldRect.origin.y;
	
	// Scaling from size of old rect to size of new rect
	GLfloat sw = newRect.size.width / oldRect.size.width;
	GLfloat sh = newRect.size.height / oldRect.size.height;
	
	// Iterate the vertices moving point in old rect to point in new rect.
	// Each of the current U & V are reverted back to the value they would
	// naturally have, without a rectangle,
	for (GLsizei i = 0; i < elementCount; i++) 
	{
		cocos2d::ccTex2F* ptc = (cocos2d::ccTex2F*)addressOfElement(i);
		
		GLfloat natU = ptc->u / natWidth;
		ptc->u = (nx + ((natU - ox) * sw)) * natWidth;
		
		GLfloat natV = ptc->v / natHeight;
		ptc->v = (ny + ((natV - oy) * sh)) * natHeight;
	}
}

cocos2d::CCSize CC3VertexTextureCoordinates::getNaturalMapSize()
{
	if (cocos2d::CCSize::CCSizeEqualToSize(naturalMapSize, cocos2d::CCSizeZero)) 
	{
		for (GLsizei i = 0; i < elementCount; i++) 
		{
			cocos2d::ccTex2F tc = texCoord2FAt(i);
			naturalMapSize.width = MAX(tc.u, naturalMapSize.width);
			naturalMapSize.height = MAX(tc.v, naturalMapSize.height);
		}
	}
	
	return naturalMapSize;
}

cocos2d::ccTex2F CC3VertexTextureCoordinates::texCoord2FAt(GLsizei index)
{
	return *(cocos2d::ccTex2F*)addressOfElement(index);
}

bool CC3VertexTextureCoordinates::initWithTag(GLuint aTag, const char* aName)
{
	if ( CC3VertexArray::initWithTag(aTag, aName) ) 
	{
		elementSize = 2;
		naturalMapSize = cocos2d::CCSizeZero;
		textureRectangle = kCC3UnitTextureRectangle;
		return true;
	}
	
	return false;
}

void CC3VertexTextureCoordinates::populateFrom(CC3VertexTextureCoordinates* another)
{
	CC3VertexArray::populateFrom(another);
	
	naturalMapSize = another->getNaturalMapSize();
	textureRectangle = another->getTextureRectangle();
}

void CC3VertexTextureCoordinates::setTexCoord2F(cocos2d::ccTex2F aTex2F, GLsizei index)
{
	*(cocos2d::ccTex2F*)addressOfElement(index) = aTex2F;
}

void CC3VertexTextureCoordinates::alignWithTextureMapSize(cocos2d::ccTex2F texMapSize)
{
	naturalMapSize = cocos2d::CCSizeZero;
	
	for (GLsizei i = 0; i < elementCount; i++) 
	{
		cocos2d::ccTex2F* ptc = (cocos2d::ccTex2F*)addressOfElement(i);
		ptc->u *= texMapSize.u;
		ptc->v *= texMapSize.v;
		
		// While we are remapping, measure the resulting map size at the same time
		naturalMapSize.width = MAX(ptc->u, naturalMapSize.width);
		naturalMapSize.height = MAX(ptc->v, naturalMapSize.height);
	}
}

void CC3VertexTextureCoordinates::alignWithInvertedTextureMapSize(cocos2d::ccTex2F texMapSize)
{
	naturalMapSize = cocos2d::CCSizeZero;
	
	for (GLsizei i = 0; i < elementCount; i++) 
	{
		cocos2d::ccTex2F* ptc = (cocos2d::ccTex2F*)addressOfElement(i);
		ptc->u *= texMapSize.u;
		ptc->v = (1.0 - ptc->v) * texMapSize.v;
		
		// While we are remapping, measure the resulting map size at the same time
		naturalMapSize.width = MAX(ptc->u, naturalMapSize.width);
		naturalMapSize.height = MAX(ptc->v, naturalMapSize.height);
	}
}

void CC3VertexTextureCoordinates::alignWithTexture(CC3Texture* texture)
{
	if (texture) 
	{
		alignWithTextureMapSize(texture->getMapSize());
	}
}

void CC3VertexTextureCoordinates::alignWithInvertedTexture(CC3Texture* texture)
{
	if (texture) 
	{
		alignWithInvertedTextureMapSize(texture->getMapSize());
	}
}

void CC3VertexTextureCoordinates::flipHorizontally()
{
	GLfloat minU = FLT_MAX;
	GLfloat maxU = -FLT_MAX;
	for (GLsizei i = 0; i < elementCount; i++) 
	{
		cocos2d::ccTex2F* ptc = (cocos2d::ccTex2F*)addressOfElement(i);
		minU = MIN(ptc->u, minU);
		maxU = MAX(ptc->u, maxU);
	}
	for (GLsizei i = 0; i < elementCount; i++) 
	{
		cocos2d::ccTex2F* ptc = (cocos2d::ccTex2F*)addressOfElement(i);
		ptc->u = minU + maxU - ptc->u;
	}
}

void CC3VertexTextureCoordinates::flipVertically()
{
	GLfloat minV = FLT_MAX;
	GLfloat maxV = -FLT_MAX;
	for (GLsizei i = 0; i < elementCount; i++) 
	{
		cocos2d::ccTex2F* ptc = (cocos2d::ccTex2F*)addressOfElement(i);
		minV = MIN(ptc->v, minV);
		maxV = MAX(ptc->v, maxV);
	}
	for (GLsizei i = 0; i < elementCount; i++) 
	{
		cocos2d::ccTex2F* ptc = (cocos2d::ccTex2F*)addressOfElement(i);
		ptc->v = minV + maxV - ptc->v;
	}
}

void CC3VertexTextureCoordinates::unbind(GLuint textureUnit)
{
	//LogTrace(@"Unbinding texture unit %u", textureUnit);
	CC3OpenGLES11TextureUnit* gles11TexUnit = CC3OpenGLES11Engine::engine()->getTextures()->textureUnitAt(textureUnit);
	gles11TexUnit->getTextureCoordArray()->disable();
}

void CC3VertexTextureCoordinates::unbindRemainingFrom(GLuint textureUnit)
{
	GLuint maxTexUnits = CC3OpenGLES11Engine::engine()->getTextures()->textureUnitCount();
	for (int tu = textureUnit; tu < maxTexUnits; tu++) 
	{
		unbind(tu);
	}
}

void CC3VertexTextureCoordinates::unbind()
{
	unbindRemainingFrom(0);
}

void CC3VertexTextureCoordinates::bindPointer(GLvoid* pointer, CC3NodeDrawingVisitor* visitor)
{
	CC3OpenGLES11TextureUnit* gles11TexUnit = CC3OpenGLES11Engine::engine()->getTextures()->textureUnitAt(visitor->getTextureUnit());
	gles11TexUnit->getTextureCoordArray()->enable();
	gles11TexUnit->getTextureCoordinates()->useElementsAt(pointer,
														  elementSize,
														  elementType,
														  elementStride);
	//LogTrace(@"%@ bound to %@", self, gles11TexUnit);
}

bool CC3VertexTextureCoordinates::switchingArray()
{
	return true;
}

//@interface CC3VertexTextureCoordinates (TemplateMethods)
//@property(nonatomic, readonly) CGSize naturalMapSize;
//-(void) alignWithTextureRectangle: (CGRect) newRect fromOld: (CGRect) oldRect;
//@end
//
//@implementation CC3VertexTextureCoordinates
//
//-(CGRect) textureRectangle {
//	return textureRectangle;
//}
//
//-(void) setTextureRectangle: (CGRect) aRect {
//	CGRect oldRect = textureRectangle;
//	textureRectangle = aRect;
//	[self alignWithTextureRectangle: aRect fromOld: oldRect];
//}
//
//-(id) initWithTag: (GLuint) aTag withName: (NSString*) aName {
//	if ( (self = [super initWithTag: aTag withName: aName]) ) {
//		elementSize = 2;
//		naturalMapSize = CGSizeZero;
//		textureRectangle = kCC3UnitTextureRectangle;
//	}
//	return self;
//}
//
//// Template method that populates this instance from the specified other instance.
//// This method is invoked automatically during object copying via the copyWithZone: method.
//-(void) populateFrom: (CC3VertexTextureCoordinates*) another {
//	[super populateFrom: another];
//	
//	naturalMapSize = another.naturalMapSize;
//	textureRectangle = another.textureRectangle;
//}
//
//-(ccTex2F) texCoord2FAt: (GLsizei) index {
//	return *(ccTex2F*)[self addressOfElement: index];
//}
//
//-(void) setTexCoord2F: (ccTex2F) aTex2F at: (GLsizei) index {
//	*(ccTex2F*)[self addressOfElement: index] = aTex2F;
//}
//
///** Extracts the current texture unit from the visitor and binds this vertex array to that texture unit. */
//-(void) bindPointer: (GLvoid*) pointer withVisitor: (CC3NodeDrawingVisitor*) visitor {
//	CC3OpenGLES11TextureUnit* gles11TexUnit = [[CC3OpenGLES11Engine engine].textures textureUnitAt: visitor.textureUnit];
//	[gles11TexUnit.textureCoordArray enable];
//	[gles11TexUnit.textureCoordinates useElementsAt: pointer
//										   withSize: elementSize
//										   withType: elementType
//										 withStride: elementStride];
//	LogTrace(@"%@ bound to %@", self, gles11TexUnit);
//}
//
//+(void) unbind: (GLuint) textureUnit {
//	LogTrace(@"Unbinding texture unit %u", textureUnit);
//	CC3OpenGLES11TextureUnit* gles11TexUnit = [[CC3OpenGLES11Engine engine].textures textureUnitAt: textureUnit];
//	[gles11TexUnit.textureCoordArray disable];
//}
//
///**
// * Unbinds all texture units between the specified texture unit index and the maximum number
// * of texture units supported by the platform. This is a convenience method for disabling
// * unused texture units.
// */
//+(void) unbindRemainingFrom: (GLuint)textureUnit {
//	GLuint maxTexUnits = [CC3OpenGLES11Engine engine].textures.textureUnitCount;
//	for (int tu = textureUnit; tu < maxTexUnits; tu++) {
//		[self unbind: tu];
//	}
//}
//
//+(void) unbind {
//	[self unbindRemainingFrom: 0];
//}
//
///**
// * Returns the size of the map as a fraction between zero and one. This indicates
// * how much of the texture is covered by this texture coordinate map when the
// * texture is simply overlaid on the mesh, before any textureRectangle is applied.
// *
// * If the value of this property has not yet been measured by one of the
// * alignWith...Texture...: methods, it is measured when first accessed here.
// */
//-(CGSize) naturalMapSize {
//	if (CGSizeEqualToSize(naturalMapSize, CGSizeZero)) {
//		for (GLsizei i = 0; i < elementCount; i++) {
//			ccTex2F tc = [self texCoord2FAt: i];
//			naturalMapSize.width = MAX(tc.u, naturalMapSize.width);
//			naturalMapSize.height = MAX(tc.v, naturalMapSize.height);
//		}
//	}
//	return naturalMapSize;
//}
//
///**
// * Aligns the vertex texture coordinates with the area of the texture defined
// * by the newRect. The oldRect describes the area of the texture that is currently
// * mapped by the texture coordinates.
// */
//-(void) alignWithTextureRectangle: (CGRect) newRect fromOld: (CGRect) oldRect {
//
//	// The size of the mapping in its natural state, before slicing out a rectangle 
//	GLfloat natWidth = self.naturalMapSize.width;
//	GLfloat natHeight = self.naturalMapSize.height;
//	
//	// Origin of new rect
//	GLfloat nx = newRect.origin.x;
//	GLfloat ny = newRect.origin.y;
//	
//	// Origin of old rect
//	GLfloat ox = oldRect.origin.x;
//	GLfloat oy = oldRect.origin.y;
//	
//	// Scaling from size of old rect to size of new rect
//	GLfloat sw = newRect.size.width / oldRect.size.width;
//	GLfloat sh = newRect.size.height / oldRect.size.height;
//	
//	// Iterate the vertices moving point in old rect to point in new rect.
//	// Each of the current U & V are reverted back to the value they would
//	// naturally have, without a rectangle,
//	for (GLsizei i = 0; i < elementCount; i++) {
//		ccTex2F* ptc = (ccTex2F*)[self addressOfElement: i];
//
//		GLfloat natU = ptc->u / natWidth;
//		ptc->u = (nx + ((natU - ox) * sw)) * natWidth;
//
//		GLfloat natV = ptc->v / natHeight;
//		ptc->v = (ny + ((natV - oy) * sh)) * natHeight;
//	}
//}
//
//-(void) alignWithTextureMapSize: (ccTex2F) texMapSize {
//	naturalMapSize = CGSizeZero;
//
//	for (GLsizei i = 0; i < elementCount; i++) {
//		ccTex2F* ptc = (ccTex2F*)[self addressOfElement: i];
//		ptc->u *= texMapSize.u;
//		ptc->v *= texMapSize.v;
//
//		// While we are remapping, measure the resulting map size at the same time
//		naturalMapSize.width = MAX(ptc->u, naturalMapSize.width);
//		naturalMapSize.height = MAX(ptc->v, naturalMapSize.height);
//	}
//}
//
//-(void) alignWithInvertedTextureMapSize: (ccTex2F) texMapSize {
//	naturalMapSize = CGSizeZero;
//
//	for (GLsizei i = 0; i < elementCount; i++) {
//		ccTex2F* ptc = (ccTex2F*)[self addressOfElement: i];
//		ptc->u *= texMapSize.u;
//		ptc->v = (1.0 - ptc->v) * texMapSize.v;
//		
//		// While we are remapping, measure the resulting map size at the same time
//		naturalMapSize.width = MAX(ptc->u, naturalMapSize.width);
//		naturalMapSize.height = MAX(ptc->v, naturalMapSize.height);
//	}
//}
//
//-(void) alignWithTexture: (CC3Texture*) texture {
//	if (texture) {
//		[self alignWithTextureMapSize: texture.mapSize];
//	}
//}
//
//-(void) alignWithInvertedTexture: (CC3Texture*) texture {
//	if (texture) {
//		[self alignWithInvertedTextureMapSize: texture.mapSize];
//	}
//}
//
//-(void) flipHorizontally {
//	GLfloat minU = CGFLOAT_MAX;
//	GLfloat maxU = -CGFLOAT_MAX;
//	for (GLsizei i = 0; i < elementCount; i++) {
//		ccTex2F* ptc = (ccTex2F*)[self addressOfElement: i];
//		minU = MIN(ptc->u, minU);
//		maxU = MAX(ptc->u, maxU);
//	}
//	for (GLsizei i = 0; i < elementCount; i++) {
//		ccTex2F* ptc = (ccTex2F*)[self addressOfElement: i];
//		ptc->u = minU + maxU - ptc->u;
//	}
//}
//
//-(void) flipVertically {
//	GLfloat minV = CGFLOAT_MAX;
//	GLfloat maxV = -CGFLOAT_MAX;
//	for (GLsizei i = 0; i < elementCount; i++) {
//		ccTex2F* ptc = (ccTex2F*)[self addressOfElement: i];
//		minV = MIN(ptc->v, minV);
//		maxV = MAX(ptc->v, maxV);
//	}
//	for (GLsizei i = 0; i < elementCount; i++) {
//		ccTex2F* ptc = (ccTex2F*)[self addressOfElement: i];
//		ptc->v = minV + maxV - ptc->v;
//	}
//}
//
//
//#pragma mark Array context switching
//
///**
// * Returns whether this vertex array is different than the vertex array of the same type
// * that was most recently bound to the GL engine. To improve performance, vertex arrays
// * are only bound if they need to be.
// *
// * Because the same instance of CC3VertexTextureCoordinates can be used by multiple
// * texture units, this property always returns YES, so that the texture array will be
// * bound to the GL engine every time.
// */
//-(BOOL) switchingArray {
//	return YES;
//}
//
//@end


#pragma mark -
#pragma mark CC3VertexPointSizes

CC3VertexPointSizes::CC3VertexPointSizes()
{
}

CC3VertexPointSizes::~CC3VertexPointSizes()
{
}

bool CC3VertexPointSizes::initWithTag(GLuint aTag, const char* aName)
{
	if ( CC3VertexArray::initWithTag(aTag, aName) ) 
	{
		elementSize = 1;
		return true;
	}
	
	return false;
}

void CC3VertexPointSizes::bindPointer(GLvoid* pointer, CC3NodeDrawingVisitor* visitor)
{
	CC3OpenGLES11Engine::engine()->getVertices()->getPointSizes()->useElementsAt(pointer,
																				 elementType,
																				 elementStride);
	CC3OpenGLES11Engine::engine()->getClientCapabilities()->getPointSizeArray()->enable();
}

void CC3VertexPointSizes::unbind()
{
	CC3OpenGLES11Engine::engine()->getClientCapabilities()->getPointSizeArray()->disable();
	resetSwitching();
}

static GLuint currentPointSizesTag = 0;

bool CC3VertexPointSizes::switchingArray()
{
	bool shouldSwitch = currentPointSizesTag != tag;
	currentPointSizesTag = tag;		// Set anyway - either it changes or it doesn't.
	return shouldSwitch;
}

void CC3VertexPointSizes::resetSwitching()
{
	currentPointSizesTag = 0;
}

//@implementation CC3VertexPointSizes
//
//-(id) initWithTag: (GLuint) aTag withName: (NSString*) aName {
//	if ( (self = [super initWithTag: aTag withName: aName]) ) {
//		elementSize = 1;
//	}
//	return self;
//}
//
//-(void) bindPointer: (GLvoid*) pointer withVisitor: (CC3NodeDrawingVisitor*) visitor {
//	[[CC3OpenGLES11Engine engine].vertices.pointSizes useElementsAt: pointer
//														   withType: elementType
//														 withStride: elementStride];
//	[[CC3OpenGLES11Engine engine].clientCapabilities.pointSizeArray enable];
//}
//
//+(void) unbind {
//	[[CC3OpenGLES11Engine engine].clientCapabilities.pointSizeArray disable];
//	[self resetSwitching];
//}
//
//
//#pragma mark Array context switching
//
//// The tag of the array that was most recently drawn to the GL engine.
//// The GL engine is only updated when an array of the same type with a different tag is presented.
//// This allows for optimization by ordering the drawing of objects so that objects with
//// the same arrays are drawn together, to minimize context switching within the GL engine.
//static GLuint currentPointSizesTag = 0;
//
//-(BOOL) switchingArray {
//	BOOL shouldSwitch = currentPointSizesTag != tag;
//	currentPointSizesTag = tag;		// Set anyway - either it changes or it doesn't.
//	return shouldSwitch;
//}
//
//+(void) resetSwitching {
//	currentPointSizesTag = 0;
//}
//
//@end


#pragma mark -
#pragma mark CC3VertexIndices

CC3VertexIndices::CC3VertexIndices()
{
}

CC3VertexIndices::~CC3VertexIndices()
{
}

CC3VertexIndices* CC3VertexIndices::vertexArrayWithName(const char* aName)
{
	CC3VertexIndices* pArray = new CC3VertexIndices;
	if (pArray) 
	{
		pArray->initWithName(aName);
		pArray->autorelease();
	}
	
	return pArray;
}

GLushort CC3VertexIndices::indexAt(GLsizei index)
{
	GLvoid* ptr = addressOfElement(index);
	return elementType == GL_UNSIGNED_BYTE ? *(GLubyte*)ptr : *(GLushort*)ptr;
}

void CC3VertexIndices::setIndex(GLushort vertexIndex, GLsizei index)
{
	GLvoid* ptr = addressOfElement(index);
	if (elementType == GL_UNSIGNED_BYTE) 
	{
		*(GLubyte*)ptr = vertexIndex;
	} 
	else 
	{
		*(GLushort*)ptr = vertexIndex;
	}
}

GLenum CC3VertexIndices::getBufferTarget()
{
	return GL_ELEMENT_ARRAY_BUFFER;
}

GLuint CC3VertexIndices::getFirstElement()
{
	return bufferID ? elementOffset : ((GLuint)elements + elementOffset);
}

bool CC3VertexIndices::initWithTag(GLuint aTag, const char* aName)
{
	if ( CC3DrawableVertexArray::initWithTag(aTag, aName) ) 
	{
		elementType = GL_UNSIGNED_SHORT;
		return true;
	}
	
	return false;
}

void CC3VertexIndices::bindGLWithVisitor(CC3NodeDrawingVisitor* visitor)
{
	if (bufferID) 
	{									// use GL buffer if it exists
		//LogTrace(@"%@ binding GL buffer", self);
		CC3OpenGLES11Engine::engine()->getVertices()->bufferBinding(getBufferTarget())->setValue(bufferID);
	} 
	else if (elementCount && elements) 
	{			// use local client array if it exists
		//LogTrace(@"%@ using local array", self);
		CC3OpenGLES11Engine::engine()->getVertices()->bufferBinding(getBufferTarget())->unbind();
	} 
	else 
	{
		//LogTrace(@"%@ no elements to bind", self);
	}
}

void CC3VertexIndices::unbind()
{
	resetSwitching();
}

void CC3VertexIndices::drawStripOfLength(GLuint stripLen, GLuint startOfStrip)
{
	//LogTrace(@"%@ drawing %u indices", self, stripLen);
	CC3OpenGLES11Engine::engine()->getVertices()->drawIndicies((GLvoid*)startOfStrip,
																 stripLen,
																 elementType,
																 drawingMode);
}

#pragma mark Array context switching

static GLuint currentIndicesTag = 0;

bool CC3VertexIndices::switchingArray()
{
	bool shouldSwitch = currentIndicesTag != tag;
	currentIndicesTag = tag;		// Set anyway - either it changes or it doesn't.
	return shouldSwitch;
}

void CC3VertexIndices::resetSwitching()
{
	currentIndicesTag = 0;
}

//@implementation CC3VertexIndices
//
//-(GLenum) bufferTarget {
//	return GL_ELEMENT_ARRAY_BUFFER;
//}
//
//-(GLuint) firstElement {
//	return bufferID ? elementOffset : ((GLuint)elements + elementOffset);
//}
//
//-(id) initWithTag: (GLuint) aTag withName: (NSString*) aName {
//	if ( (self = [super initWithTag: aTag withName: aName]) ) {
//		elementType = GL_UNSIGNED_SHORT;
//	}
//	return self;
//}
//
//-(GLushort) indexAt: (GLsizei) index {
//	GLvoid* ptr = [self addressOfElement: index];
//	return elementType == GL_UNSIGNED_BYTE ? *(GLubyte*)ptr : *(GLushort*)ptr;
//}
//
//-(void) setIndex: (GLushort) vertexIndex at: (GLsizei) index {
//	GLvoid* ptr = [self addressOfElement: index];
//	if (elementType == GL_UNSIGNED_BYTE) {
//		*(GLubyte*)ptr = vertexIndex;
//	} else {
//		*(GLushort*)ptr = vertexIndex;
//	}
//}
//
//-(void) bindGLWithVisitor: (CC3NodeDrawingVisitor*) visitor {
//	if (bufferID) {									// use GL buffer if it exists
//		LogTrace(@"%@ binding GL buffer", self);
//		[[CC3OpenGLES11Engine engine].vertices bufferBinding: self.bufferTarget].value = bufferID;
//	} else if (elementCount && elements) {			// use local client array if it exists
//		LogTrace(@"%@ using local array", self);
//		[[[CC3OpenGLES11Engine engine].vertices bufferBinding: self.bufferTarget] unbind];
//	} else {
//		LogTrace(@"%@ no elements to bind", self);
//	}
//}
//
//+(void) unbind {
//	[self resetSwitching];
//}
//
//-(void) drawStripOfLength: (GLuint) stripLen startingAt: (GLuint) startOfStrip {
//	LogTrace(@"%@ drawing %u indices", self, stripLen);
//	[[CC3OpenGLES11Engine engine].vertices drawIndicies: (GLvoid*)startOfStrip
//											   ofLength: stripLen
//												andType: elementType
//													 as: drawingMode];
//} 
//
//
//#pragma mark Array context switching
//
//// The tag of the array that was most recently drawn to the GL engine.
//// The GL engine is only updated when an array of the same type with a different tag is presented.
//// This allows for optimization by ordering the drawing of objects so that objects with
//// the same arrays are drawn together, to minimize context switching within the GL engine.
//static GLuint currentIndicesTag = 0;
//
//-(BOOL) switchingArray {
//	BOOL shouldSwitch = currentIndicesTag != tag;
//	currentIndicesTag = tag;		// Set anyway - either it changes or it doesn't.
//	return shouldSwitch;
//}
//
//+(void) resetSwitching {
//	currentIndicesTag = 0;
//}
//
//@end


#pragma mark -
#pragma mark CC3IndexRunLengthArray

CC3VertexRunLengthIndices::CC3VertexRunLengthIndices()
{
}

CC3VertexRunLengthIndices::~CC3VertexRunLengthIndices()
{
}

void CC3VertexRunLengthIndices::createGLBuffer()
{
}

void CC3VertexRunLengthIndices::bindGLWithVisitor(CC3NodeDrawingVisitor* visitor)
{
	//LogTrace(@"%@ using local array", self);
	CC3OpenGLES11Engine::engine()->getVertices()->bufferBinding(getBufferTarget())->unbind();
}

void CC3VertexRunLengthIndices::drawWithVisitor(CC3NodeDrawingVisitor* visitor)
{
	//LogTrace(@"%@ drawing %u indices using local run-length array", self, elementCount);
	GLubyte* elementsAsBytes = (GLubyte*) elements;
	GLushort* elementsAsShorts = (GLushort*) elements;
	switch (elementType) 
	{
		case GL_UNSIGNED_BYTE:
			for(int i = 0; i < elementCount; i += elementsAsBytes[i] + 1) 
			{
				CC3OpenGLES11Engine::engine()->getVertices()->drawIndicies(&elementsAsBytes[i+1],
																			 elementsAsBytes[i],
																			 elementType,
																			 drawingMode);
				visitor->getPerformanceStatistics()->addSingleCallFacesPresented(getFaceCountFromVertexCount(elementsAsBytes[i]));
			}
			break;
		case GL_UNSIGNED_SHORT:
			for(int i = 0; i < elementCount; i += elementsAsShorts[i] + 1) 
			{
				CC3OpenGLES11Engine::engine()->getVertices()->drawIndicies(&elementsAsShorts[i+1],
																			 elementsAsShorts[i],
																			 elementType,
																			 drawingMode);
				visitor->getPerformanceStatistics()->addSingleCallFacesPresented(getFaceCountFromVertexCount(elementsAsShorts[i]));
			}
			break;
		default:
			//LogError(@"Illegal index element type in %@: %u", self, elementType);
			break;
	}	
}

//@implementation CC3VertexRunLengthIndices
//
//// Since we want to use a run-length encoded index array, we need local control, so don't attempt to create a buffer
//-(void) createGLBuffer {}
//
//// Since we want to use a run-length encoded index array, we need local control, so remove any buffer binding.
//-(void) bindGLWithVisitor: (CC3NodeDrawingVisitor*) visitor {
//	LogTrace(@"%@ using local array", self);
//	[[[CC3OpenGLES11Engine engine].vertices bufferBinding: self.bufferTarget] unbind];
//}
//
//// Draws the mesh using a RLE (run-length encoded) index array
//-(void) drawWithVisitor: (CC3NodeDrawingVisitor*) visitor {
//	LogTrace(@"%@ drawing %u indices using local run-length array", self, elementCount);
//	GLubyte* elementsAsBytes = (GLubyte*) elements;
//	GLushort* elementsAsShorts = (GLushort*) elements;
//	switch (elementType) {
//		case GL_UNSIGNED_BYTE:
//			for(int i = 0; i < elementCount; i += elementsAsBytes[i] + 1) {
//				[[CC3OpenGLES11Engine engine].vertices drawIndicies: &elementsAsBytes[i+1]
//														   ofLength: elementsAsBytes[i]
//															andType: elementType
//																 as: drawingMode];
//				[visitor.performanceStatistics addSingleCallFacesPresented: [self faceCountFromVertexCount: elementsAsBytes[i]]];
//			}
//			break;
//		case GL_UNSIGNED_SHORT:
//			for(int i = 0; i < elementCount; i += elementsAsShorts[i] + 1) {
//				[[CC3OpenGLES11Engine engine].vertices drawIndicies: &elementsAsShorts[i+1]
//														   ofLength: elementsAsShorts[i]
//															andType: elementType
//																 as: drawingMode];
//				[visitor.performanceStatistics addSingleCallFacesPresented: [self faceCountFromVertexCount: elementsAsShorts[i]]];
//			}
//			break;
//		default:
//			LogError(@"Illegal index element type in %@: %u", self, elementType);
//			break;
//	}	
//}
//
//@end
